package com.ecr.hub.service.data.input.mail.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.search.FlagTerm;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.ecr.hub.database.DaoException;
import com.ecr.hub.database.dao.IDaoInputData;
import com.ecr.hub.model.HubInputData;
import com.ecr.hub.model.enumeration.HubDataInputStatus;
import com.ecr.hub.model.enumeration.HubTypeFile;
import com.ecr.hub.service.data.input.mail.IInboxReader;
import com.ecr.hub.service.data.input.xls.IXlsDecoder;

@Component("inboxReader")
public class InboxReaderImpl implements IInboxReader {

	@Resource(name = "daoInputData")
	IDaoInputData daoInputData;

	private static final Logger log = Logger.getLogger(InboxReaderImpl.class);

	private static final int SIZE_BUFFER = 1024;

	// La ressource pour le decodeur Xls.
	@Resource(name = "xlsDecoder")
	private IXlsDecoder xlsDecoder;

	// Récupération des différents paramètres pour la reception des mails.
	@Value(value = "${mail.store.protocol}")
	private String mailStoreProtocole;
	@Value(value = "${mail.store.host}")
	private String mailStoreHost;
	@Value(value = "${mail.debug}")
	private String mailDebug;
	@Value(value = "${mail.user}")
	private String mailUser;
	@Value(value = "${mail.password}")
	private String mailPassword;
	@Value(value = "${mail.folder.name}")
	private String mailFolderName;
	@Value(value = "${repository.folder}")
	private String repositoryFolder;

	@PostConstruct
	public void checkRepository() {

		File repFile = new File(repositoryFolder);
		if (!repFile.exists()) {
			repFile.mkdirs();
		}
	}

	/**
	 * Méthode principale, lancée par le cron dans le fichier de configuration
	 * de Spring. On intéroge la boîte et on récupère l'ensemble des mails avec
	 * les pieces jointes.
	 * 
	 * Pour chaque mail, on regarde la / les pieces (pour l'instant la règle est
	 * une pièce par mail) et on renomme le piece jointe avec un nom spécifique
	 * pour l'aplication. Enfin on enregistre la pièce dans le repertoire de
	 * stockage et on crée une ligne de traitement dans la table hub_input_data.
	 */
	public void retrieveFiles() {

		// Récupéation de la session mail par défaut.
		Properties props = new Properties();
		props.setProperty("mail.store.protocol", mailStoreProtocole);
		props.put("mail.debug", mailDebug);

		log.info("Analyse de la boîte mail : " + mailUser);

		Session session = Session.getDefaultInstance(props, null);
		Store store = null;

		try {
			store = session.getStore();
			store.connect(mailStoreHost, mailUser, mailPassword);
			Folder folder = store.getFolder(mailFolderName);
			folder.open(Folder.READ_WRITE);
			FlagTerm ft = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
			Message[] messages = folder.search(ft);
			// Parcourt de la liste des mails.
			for (Message message : messages) {
				treatMessage(message);
			}
		} catch (NoSuchProviderException e) {
			log.fatal("Le service mail n'est pas disponible : "
					+ e.getMessage());
		} catch (MessagingException e) {
			log.fatal("Erreur lors du traitement de mail : " + e.getMessage());
		} finally {
			// On s'assure que la connection est bien fermé
			if (store != null)
				try {
					store.close();
				} catch (MessagingException e) {
					log.error("Erreur lors de la fermeture de la connexion mail : "
							+ e.getMessage());
				}
		}
	}

	/**
	 * Traitement unitaire d'un message. On parse le message, on récupère la
	 * piece jointe, on vérifie si elle a une extension xls et on enregistre la
	 * pièce jointe. Enfin on ajoute une ligne de traitement dans la table
	 * hub_input_data.
	 */
	private void treatMessage(Message message) {

		// Lecture d'un mail.
		HubInputData inputData = new HubInputData();

		// Insertion de la date de traitement dans l'objet de stockage.
		inputData.setDateProcessing(new Date());

		try {
			message.setFlag(Flags.Flag.SEEN, true);
			String subject = message.getSubject();

			log.info("Lecture mail : sujet = " + subject.toString());

			Object content = message.getContent();
			if (content instanceof Multipart) {

				Multipart mp = (Multipart) content;

				for (int i = 0, n = mp.getCount(); i < n; i++) {
					Part part = mp.getBodyPart(i);

					String disposition = part.getDisposition();

					if ((disposition != null)
							&& disposition.equalsIgnoreCase(Part.ATTACHMENT)) {
						String attachedFileName = part.getFileName();

						if (attachedFileName.endsWith(IXlsDecoder.EXCEL_SUFFIX)) {

							log.info("Fichier attaché trouvé : "
									+ part.getFileName());

							// On continue de remplir l'objet de stockage avec
							// les données issues du fichier.
							inputData.setFileAttachedName(part.getFileName());
							inputData.setStatus(HubDataInputStatus.FILE_VALID);

							// Sauvegarde du fichier.
							File dir = new File(repositoryFolder);
							if (!dir.exists())
								dir.mkdir();

							// Construction du nom de stockage pour le fichier.
							String fileStoreName;
							if (part.getFileName().contains("Consist")) {
								fileStoreName = xlsDecoder
										.getStoreFileNameXls(HubTypeFile.COMPOSITION);
							} else {
								// Si ce n'est ni un fichier de sillon ni un
								// fichier de composition, dans tous les cas on
								// aura une erreur au décodage du fichier.
								fileStoreName = xlsDecoder
										.getStoreFileNameXls(HubTypeFile.SILLON);
							}

							// Construction du chemin complet pour l'écriture
							// du fichier.
							String filePath = repositoryFolder + fileStoreName;

							// Ecriture du fichier dans le répertoire.
							saveAttachedFile(filePath, part.getInputStream());

							// On complète l'objet de stockage avec le nom du
							// fichier stocké.
							inputData.setFileStoreName(fileStoreName);
							
							//On sort de la boucle.
							break;
						} else {
							log.error("Fichier au format xls non trouvé en pièce jointe !");
							inputData
									.setStatus(HubDataInputStatus.ERROR_MAIL_FILE_NOT_XLS);
						}
					} else {
						log.info("Fichier non trouvé, passe à la section suivante du mail...");
						inputData
								.setStatus(HubDataInputStatus.ERROR_MAIL_FILE_NOT_FOUND);
					}
				}
			} else {
				log.error("Aucun fichier présent dans le mail.");
				inputData
						.setStatus(HubDataInputStatus.ERROR_MAIL_FILE_NOT_FOUND);
			}
		} catch (MessagingException e) {
			log.fatal("Traitement du mail une erreur est survenue : "
					+ e.getMessage());
			inputData.setStatus(HubDataInputStatus.ERROR_MAIL_READING);

		} catch (IOException e) {
			log.fatal("Traitement du mail une erreur est survenue : "
					+ e.getMessage());
			inputData.setStatus(HubDataInputStatus.ERROR_XLS_WRITING);
		} finally {

			try {
				// Dans tous les cas, création nouvelle ligne de traitement en
				// base pour conserver un trace du traitement (si erreur) et
				// pour piloter le décodage du fichier par la suite.
				daoInputData.insert(inputData);

				// Dans tous les cas, on supprime le message de la boîte mail.
				// (un message sans pièce jointe ne nous sert absolument à rien,
				// inutile de le conserver).
				message.setFlag(Flags.Flag.DELETED, true);

			} catch (DaoException e) {
				log.fatal("Hub : Impossible de mettre à jour la table hub_input_data : "
						+ e.getMessage());

			} catch (MessagingException e) {
				log.fatal("Hub : Impossible de supprimer le message dans la boîte mail : "
						+ e.getMessage());
			}
		}
	}

	/**
	 * Sauvegarde du fichier dans le repertoire de stockage.
	 */
	public void saveAttachedFile(String storeFileName, InputStream inputStream)
			throws IOException {

		OutputStream out = null;

		try {
			File file = new File(storeFileName);
			out = new FileOutputStream(file);
			byte buf[] = new byte[SIZE_BUFFER];
			int len;

			while ((len = inputStream.read(buf)) > 0)
				out.write(buf, 0, len);

		} catch (FileNotFoundException e) {
			log.fatal("Le repertoire de stockage est introuvable ou inaccessible : "
					+ e.getMessage());

		} catch (IOException e) {
			log.fatal("Le fichier " + storeFileName
					+ " n'a pas été sauvegardé : " + e.getMessage());
			throw e;
		} finally {
			try {
				out.close();
				inputStream.close();
			} catch (IOException e) {
				// RAS.
			}
		}
	}
}